﻿using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto.Digests;

namespace ApiLib.Utils;

/// <summary>
/// 加密工具
/// </summary>
public static class EncryptUtil
{
    /// <summary>
    /// 获取Basic认证字符串，使用时Header加参数Authorization=Basic {authKey}
    /// </summary>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <returns></returns>
    public static string BasicAuth(string username, string password)
    {
        return Base64Util.Base64Encode(username + ":" + password);
    }

    /// <summary>
    /// 生成随机密码
    /// </summary>
    /// <returns></returns>
    public static string GenerateRandomSecret(int length = 64)
    {
        // ReSharper disable StringLiteralTypo
        var str = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789.,#@&+-?()";
        // ReSharper restore StringLiteralTypo
        var sb = new StringBuilder();
        var random = new Random();
        for (var i = 0; i < length; i++)
        {
            sb.Append(str[random.Next(0, 72)]);
        }

        return sb.ToString();
    }

    /// <summary>
    /// 随机密钥：16=128bit，24=192bit，32=256bit
    /// </summary>
    /// <param name="length"></param>
    /// <returns></returns>
    public static byte[] GenerateRandomKey(int length)
    {
        using var rng = RandomNumberGenerator.Create();
        var key = new byte[length];
        rng.GetBytes(key);
        return key;
    }

    /// <summary>
    /// 转换成byte[]密钥，间隔符<c>-</c>会自动替换掉
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public static byte[] TranceByteArrayKey(string key)
    {
        List<string> chars = [];
        if (key.Contains('-'))
        {
            chars = key.Split("-").ToList();
        }
        else
        {
            for (var i = 0; i < key.Length; i++)
            {
                if (i % 2 == 0)
                {
                    chars.Add("" + key[i] + key[i + 1]);
                }
            }
        }

        return chars.Select(str => Convert.ToByte(str, 16)).ToArray();
    }

    /// <summary>
    /// 转换成字符串密钥
    /// </summary>
    /// <param name="keys"></param>
    /// <returns></returns>
    public static string TranceStringKey(byte[] keys)
    {
        return BitConverter.ToString(keys).Replace("-", "").ToUpperInvariant();
    }

    /// <summary>
    /// Base64加密
    /// </summary>
    public static class Base64Util
    {
        /// <summary>
        /// Base64加密
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string Base64Encode(string text)
        {
            var bytes = Encoding.UTF8.GetBytes(text);
            return Convert.ToBase64String(bytes);
        }

        /// <summary>
        /// Base64解密
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string Base64Decode(string text)
        {
            var bytes = Convert.FromBase64String(text);
            return Encoding.UTF8.GetString(bytes);
        }
    }

    /// <summary>
    /// Hash加密，唯一性校验
    /// </summary>
    public static class HashUtil
    {
        /// <summary>
        /// MD5字符串加密，效率最高
        /// </summary>
        /// <param name="txt"></param>
        /// <returns>加密后字符串</returns>
        public static string Md5(string txt)
        {
            var buffer = Encoding.UTF8.GetBytes(txt);
            //开始加密
            return Md5(buffer);
        }

        /// <summary>
        /// MD5加密，效率最高
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns>加密后字符串</returns>
        public static string Md5(byte[] bytes)
        {
            var hashData = MD5.HashData(bytes);
            return BitConverter.ToString(hashData).Replace("-", "").ToUpperInvariant();
        }

        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        [Obsolete("请使用Md5方法")]
        public static string Md5Old2(byte[] bytes)
        {
            using var md5 = MD5.Create();
            var hashData = md5.ComputeHash(bytes);
            return BitConverter.ToString(hashData).Replace("-", "").ToUpperInvariant();
        }

        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        [Obsolete("请使用Md5方法")]
        public static string Md5Old(byte[] bytes)
        {
            using var mi = MD5.Create();
            var buffer = mi.ComputeHash(bytes);
            var sb = new StringBuilder();
            foreach (var b in buffer)
            {
                sb.Append(b.ToString("x2"));
            }

            return sb.ToString();
        }

        /// <summary>
        /// Sha1字符串加密，安全性与效率平衡
        /// </summary>
        /// <param name="txt"></param>
        /// <returns>加密后字符串</returns>
        public static string Sha1(string txt)
        {
            var buffer = Encoding.UTF8.GetBytes(txt);
            //开始加密
            return Sha1(buffer);
        }

        /// <summary>
        /// Sha1加密，安全性与效率平衡
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns>加密后字符串</returns>
        public static string Sha1(byte[] bytes)
        {
            var hashData = SHA1.HashData(bytes);
            return BitConverter.ToString(hashData).Replace("-", "").ToUpperInvariant();
        }

        /// <summary>
        /// Sha256字符串加密，安全性最高
        /// </summary>
        /// <param name="txt"></param>
        /// <returns>加密后字符串</returns>
        public static string Sha256(string txt)
        {
            var buffer = Encoding.UTF8.GetBytes(txt);
            //开始加密
            return Sha256(buffer);
        }

        /// <summary>
        /// Sha256加密，安全性最高
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns>加密后字符串</returns>
        public static string Sha256(byte[] bytes)
        {
            var hashData = SHA256.HashData(bytes);
            return BitConverter.ToString(hashData).Replace("-", "").ToUpperInvariant();
        }

        /// <summary>
        /// Sha512字符串加密
        /// </summary>
        /// <param name="txt"></param>
        /// <returns>加密后字符串</returns>
        public static string Sha512(string txt)
        {
            var buffer = Encoding.UTF8.GetBytes(txt);
            //开始加密
            return Sha512(buffer);
        }

        /// <summary>
        /// Sha512加密
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns>加密后字符串</returns>
        public static string Sha512(byte[] bytes)
        {
            var hashData = SHA512.HashData(bytes);
            return BitConverter.ToString(hashData).Replace("-", "").ToUpperInvariant();
        }

        /// <summary>
        /// Sm3字符串加密。SM3是中国国家密码局制定的哈希算法标准，类似于SHA-256，用于生成固定长度的摘要信息。
        /// </summary>
        /// <param name="txt"></param>
        /// <returns>加密后字符串</returns>
        public static string Sm3(string txt)
        {
            var buffer = Encoding.UTF8.GetBytes(txt);
            //开始加密
            return Sm3(buffer);
        }

        /// <summary>
        /// Sm3加密。SM3是中国国家密码局制定的哈希算法标准，类似于SHA-256，用于生成固定长度的摘要信息。
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns>加密后字符串</returns>
        public static string Sm3(byte[] bytes)
        {
            var digest = new SM3Digest();
            digest.BlockUpdate(bytes, 0, bytes.Length);
            var hashData = new byte[digest.GetDigestSize()];
            digest.DoFinal(hashData, 0);
            return BitConverter.ToString(hashData).Replace("-", "").ToUpperInvariant();
        }
    }

    /// <summary>
    /// Bcrypt加密，不可逆加密
    /// </summary>
    public static class BcryptUtil
    {
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="text">文本</param>
        /// <returns></returns>
        public static string Hash(string text)
        {
            return BCrypt.Net.BCrypt.HashPassword(text);
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="text">文本</param>
        /// <param name="hash">哈希</param>
        /// <returns></returns>
        public static bool Verify(string text, string hash)
        {
            return BCrypt.Net.BCrypt.Verify(text, hash);
        }
    }

    /// <summary>
    /// RSA加密，非对称加密，公钥加密，私钥解密<br/>
    /// <code>私钥：通常以-----BEGIN RSA PRIVATE KEY-----开头，以-----END RSA PRIVATE KEY-----结尾。
    /// 公钥：以-----BEGIN PUBLIC KEY-----开始，以-----END PUBLIC KEY-----结束。
    /// 证书：以-----BEGIN CERTIFICATE-----开始，以-----END CERTIFICATE-----结束。
    /// </code>
    /// </summary>
    public static class RsaUtil
    {
        /// <summary>
        /// 新的密钥
        /// </summary>
        /// <param name="privateKey">私钥</param>
        /// <returns></returns>
        public static string NewKey(out string privateKey)
        {
            using var rsa = new RSACryptoServiceProvider();
            // 获取公钥和私钥字符串
            var publicKey = rsa.ToXmlString(false); // 不包括私钥部分
            privateKey = rsa.ToXmlString(true); // 包括私钥部分
            return publicKey;
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="plainText"></param>
        /// <param name="publicKey">公钥</param>
        /// <returns></returns>
        public static string Encrypt(string plainText, string publicKey)
        {
            return Convert.ToBase64String(EncryptToBytes(plainText, publicKey));
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="plainText"></param>
        /// <param name="publicKey">公钥</param>
        /// <returns></returns>
        public static byte[] EncryptToBytes(string plainText, string publicKey)
        {
            using var rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(publicKey);
            var dataToEncrypt = Encoding.UTF8.GetBytes(plainText);
            return rsa.Encrypt(dataToEncrypt, true);
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="cipherText"></param>
        /// <param name="privateKey">私钥</param>
        /// <returns></returns>
        public static string Decrypt(string cipherText, string privateKey)
        {
            return DecryptFromBytes(Convert.FromBase64String(cipherText), privateKey);
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="cipherText"></param>
        /// <param name="privateKey">私钥</param>
        /// <returns></returns>
        public static string DecryptFromBytes(byte[] cipherText, string privateKey)
        {
            using var rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(privateKey);
            var decryptedData = rsa.Decrypt(cipherText, true);
            return Encoding.UTF8.GetString(decryptedData);
        }
    }

    /// <summary>
    /// AES加密，对称加密，同样的密钥加密解密
    /// </summary>
    public static class AesUtil
    {
        /// <summary>
        /// 初始化向量（IV）// 128 bits
        /// </summary>
        /// <returns></returns>
        public static byte[] GenerateRandomIv()
        {
            using var aes = Aes.Create();
            aes.GenerateIV();
            return aes.IV;
        }

        /// <summary>
        /// 获取密钥和初始化向量（IV）
        /// </summary>
        /// <returns></returns>
        public static byte[] NewKey(out byte[] iv)
        {
            iv = GenerateRandomIv();
            return GenerateRandomKey(32);
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="plaintext"></param>
        /// <param name="key">密钥</param>
        /// <param name="iv">初始化向量</param>
        /// <param name="mode"></param>
        /// <param name="padding"></param>
        /// <returns></returns>
        public static string EncryptToString(string plaintext, byte[] key, byte[]? iv = null,
            CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
        {
            return TranceStringKey(Encrypt(plaintext, key, iv, mode, padding));
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="plaintext"></param>
        /// <param name="key">密钥</param>
        /// <param name="iv">初始化向量</param>
        /// <param name="mode"></param>
        /// <param name="padding"></param>
        /// <returns></returns>
        public static byte[] Encrypt(string plaintext, byte[] key, byte[]? iv = null,
            CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
        {
            using var aes = Aes.Create();
            aes.Mode = mode;
            aes.Padding = padding;
            aes.Key = key;
            if (iv == null)
            {
                aes.GenerateIV();
            }
            else
            {
                aes.IV = iv;
            }

            var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

            using var ms = new MemoryStream();
            using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
            using var sw = new StreamWriter(cs);
            sw.Write(plaintext);
            return ms.ToArray();
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="encryptedData"></param>
        /// <param name="key">密钥</param>
        /// <param name="iv">初始化向量</param>
        /// <param name="mode"></param>
        /// <param name="padding"></param>
        /// <returns></returns>
        public static string DecryptFromString(string encryptedData, byte[] key, byte[]? iv = null,
            CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
        {
            return Decrypt(TranceByteArrayKey(encryptedData), key, iv, mode, padding);
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="encryptedData"></param>
        /// <param name="key">密钥</param>
        /// <param name="iv">初始化向量</param>
        /// <param name="mode"></param>
        /// <param name="padding"></param>
        /// <returns></returns>
        public static string Decrypt(byte[] encryptedData, byte[] key, byte[]? iv = null,
            CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
        {
            using var aes = Aes.Create();
            aes.Mode = mode;
            aes.Padding = padding;
            aes.Key = key;
            if (iv == null)
            {
                aes.GenerateIV();
            }
            else
            {
                aes.IV = iv;
            }

            var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

            using var ms = new MemoryStream(encryptedData);
            using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
            using var sr = new StreamReader(cs);
            return sr.ReadToEnd();
        }
    }
}